package model.route;

import java.io.Serializable;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import model.Model;
import model.info.Constants;
import model.info.Coordinate;
import model.info.CoordinateRangeException;
import model.mapping.MapPoint;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class Route {

	private Model model;
	private ArrayList<Step> path;
	private String beginning;
	private String wayPoint;
	private String end;
	private int step;

	public Route(Model model,String beginning,String waypoint,String end) {
		
		this.model = model;
		this.path = new ArrayList<Step>();
		this.beginning = beginning;
		this.wayPoint = waypoint;
		this.end = end;
		this.step = 0;
		Document doc = getXMLRoute();
		getRouteElement(doc);
	}
	
	public ArrayList<Step> getPath() {
		return path;
	}

	public void setPath(ArrayList<Step> path) {
		this.path = path;
	}

	public String getBeginning() {
		return beginning;
	}

	public void setBeginning(String beginning) {
		this.beginning = beginning;
	}

	public String getEnd() {
		return end;
	}

	public void setEnd(String fin) {
		this.end = fin;
	}
	
	public String getWayPoint() {
		return wayPoint;
	}

	public void setWayPoint(String wayPoint) {
		this.wayPoint = wayPoint;
	}

	public int getStep() {
		return step;
	}

	public void setStep(int step) {
		this.step = step;
	}

	public void drawPath() {
		
		for (int i=0;i<path.size();i++)
			System.out.println(i+": "+path.get(i).toString());
	}

	public Document getXMLRoute() {
		
		String consulta = "http://maps.google.com/maps/api/directions/xml?origin="+beginning
		+"&waypoints="+wayPoint+"&destination="+end+"&sensor=false";
    	URL url = null;
    	Document document = null;
		try {
			url = new URL(consulta);
			URLConnection conn = url.openConnection();
			DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
	    	DocumentBuilder builder = factory.newDocumentBuilder();
	    	document = builder.parse(conn.getInputStream());
		} catch (Exception e) {
			e.printStackTrace();
		}
		return document;
	}
	
	public void getRouteElement(Document document) {
		
		Boolean found = false;
		Node node = document.getDocumentElement();
    	NodeList list = node.getChildNodes();
    	Integer cont = 0;
    	for (int i=0;i<list.getLength()&&!found;i++)
    		if (list.item(i).getNodeName().equals(Constants.ROUTE)) {
    			list = list.item(i).getChildNodes();
	    		for (int j=0;j<list.getLength()&&!found;j++) {
	    			if (list.item(j).getNodeName().equals(Constants.LEG)) {
	    				cont = cont+1;
	    				if (cont > 1) {
	    					list = list.item(j).getChildNodes();
	    					for (int k=0;k<list.getLength()&&!found;k++)
	    						if (list.item(k).getNodeName().equals(Constants.STEP)) {
	    							Node newNode = list.item(k);
	    							Element element = (Element)newNode; 
	    							Step step = generateStep(element);
	    							if (step != null)
	    								path.add(step);
	    							else
	    								found = true;
	    						}
	    				}
	    			}
	    		}
    		}
	}
	
	public Step generateStep(Element element) {
		
		String instruction = getTagValue(Constants.HTML_INSTRUCTIONS,element);
		Integer position = instruction.indexOf(Constants.OPENTRIANGLE);
		while (position != -1) {
			String part = instruction.substring(0,position);
			Integer finish = instruction.indexOf(Constants.CLOSETRIANGLE);
			String next = instruction.substring(finish+1,instruction.length());
			part = part.concat(next);
			instruction = part;
			position = instruction.indexOf(Constants.OPENTRIANGLE);
		}
		Coordinate start = getNodeValue(Constants.START_LOCATION,element);
		Coordinate end = getNodeValue(Constants.END_LOCATION,element);
		Step step = null;
		if (model.getMapCheckouts().isInMap(start)) {
			MapPoint begin = model.getMapCheckouts().getAccuratePointFromCoordinate(start);
			MapPoint finish = model.getMapCheckouts().getAccuratePointFromCoordinate(end);
			if (path.isEmpty())
				step = new Step(start,end,instruction,begin,finish);
			else if (path.get(path.size()-1).getFinishPoint().equals(begin))
				step = new Step(start,end,instruction,begin,finish);
		}	
		return step;
	}
	
	public String getTagValue(String tag, Element element) {  
	    
		NodeList list = element.getElementsByTagName(tag).item(0).getChildNodes();
		Node value = (Node)list.item(0);
		String result = new String(value.getNodeValue());
		return result;
	
	}
	
	public Coordinate getNodeValue(String tag, Element element) {
		
		Double lat = null;
		Double lng = null;
		Coordinate result = null;
		NodeList list = element.getElementsByTagName(tag).item(0).getChildNodes();
		for (int i=0;i<list.getLength();i++) {
			if (list.item(i).getNodeName().equals(Constants.LAT)) {
				Node node = list.item(i).getFirstChild();
				lat = new Double(node.getNodeValue());
			}
			else if (list.item(i).getNodeName().equals(Constants.LNG)) {
				Node node = list.item(i).getFirstChild();
				lng = new Double(node.getNodeValue());
			}
		}
		try {
			result = new Coordinate(lat,lng);
		} catch (CoordinateRangeException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	public Step getCurrentStep() {
		
		Step current = null;
		if (step < path.size()) 
			current = path.get(step);
		return current;
	}
	
	/*public boolean arrayListContains(ArrayList<Node2> c, Node2 n){
         for(int i = 0; i < c.size(); i++){
                 Node o = (Node) c.get(i);
                 if(o == n){
                         return true;
                 }
         }
         return false;
	 }

	
	public ArrayList<Node2> aStar(Node2 start, Node2 finish) {
		  
		ArrayList<Node2> open = new ArrayList<Node2>();
		ArrayList<Node2> closed = new ArrayList<Node2>();
        open.add(start);
        while(open.size() > 0) {
        	float lowest = Float.MAX_VALUE;
            int c = -1;
            for(int i=0;i<open.size();i++) {
            	Node2 temp = (Node2) open.get(i);
                if(temp.f < lowest) {
                	lowest = temp.f;
                    c = i;
                }
            }
            Node2 current = (Node2) open.remove(c);
            closed.add(current);
            if(current == finish){
            	break;
            }
            for(int i = 0; i < current.links.size(); i++) {
            	Connector a = (Connector) current.links.get(i);
                Node2 adjacent = a.n;
                if(adjacent.walkable && !arrayListContains(closed, adjacent)) {
                	if(!arrayListContains(open, adjacent)) {
                		open.add(adjacent);
                        adjacent.parent = current;
                        adjacent.setG(a);
                        adjacent.setF(finish);
                    }
                	else {
                		if(adjacent.g > current.g + a.d) {
                			adjacent.parent = current;
                            adjacent.setG(a);
                            adjacent.setF(finish);
                        }
                   }
                }
           }
        }
           // Path generation
        ArrayList<Node2> path = new ArrayList<Node2>();
        Node2 pathNode = finish;
        while(pathNode != null){
        	path.add(pathNode);
            pathNode = pathNode.parent;
        }
           // Hack to provide a compromise path when a route to the finish node is
           // unavailable
        Node test = (Node) path.get(path.size() - 1);
        if(test == finish) {
        	float leastDist = Float.MAX_VALUE;
            Node2 bestNode = null;
            for(int i=0;i<closed.size();i++) {
            	Node2 n = (Node2) closed.get(i);
                float nDist = n.dist(finish);
                if(nDist < leastDist){
                	leastDist = nDist;
                    bestNode = n;
                }
            }
            if(bestNode.parent != null){
            	pathNode = bestNode;
                path = new ArrayList<Node2>();
                while(pathNode != null){
                	path.add(pathNode);
                    pathNode = pathNode.parent;
                }
            }
        }
        return path;
   }*/
}
